//=========================================================
// INCLUDES //
//=========================================================
#include <GL/glut.h>
#include <GL/freeglut.h>
//---------------------------------------------------------
#include <math.h> //using M_PI
#include <stdio.h>
#include <string.h>
//---------------------------------------------------------
#include "draw_mine-field.hpp"
//=========================================================
// GLOBAL VARS //
//=========================================================
static int win; //window identifier
static int primitive = 0; //menu/key identifier
bool fullscreen = false;
bool mouseDown = false;
float xrot = 0.0f;
float yrot = 0.0f;
float xdiff = 0.0f;
float ydiff = 0.0f;
static double rot_angle = -600*M_PI/3600; //rotation angle
char filename[100];
char trunc_file[100];
//---------------------------------------------------------
R_matrix *R = NULL; //operator R
int dim = 0;
//=========================================================
// FUNCTION PROTOTYPES //
//=========================================================
void create_menu();
void menu(int value);
void reshape(int w, int h);
void disp();
void mouse(int button, int state, int x, int y);
void mouseMotion(int x, int y);
void keyb(unsigned char key, int x, int y);
void special_keyboard(int key, int x, int y);
void timer(int value);
void rotation(int x, int y, int z, double angle);
void scale(float x, float y, float z);
//=========================================================
// MAIN //
//=========================================================
int main(int argc, char **argv){
  if(argc<2){
    printf("Error: enter source filename: ./test <filename>\n");
    return 1;
  }else{
    glutInit(&argc, argv); //initialize glut
    //specify the display mode to be RGB, double buffering, depth buffering
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(800,600); //define the size of the window
    glutInitWindowPosition(100,50); //position where window appears
    //create the window, set the title and keep the window identifier
    win = glutCreateWindow("GLUT: Mine-Field Builder");
    create_menu(); //create menu & submenu
    glutMouseFunc(mouse);
    glutMotionFunc(mouseMotion);
    glutReshapeFunc(reshape); //set reshape callack for the current window
    glutDisplayFunc(disp); //set display callback for the current window
    glutKeyboardFunc(keyb); //set keyboard callback for the current window
    glutSpecialFunc(special_keyboard);
    //glClearColor(0.2,0.2,0.2,0.0); //define the color to clearscreen 
    glClearColor(0.0,0.0,0.0,1.0);
    glEnable(GL_AUTO_NORMAL); //generate normal vectors
    glEnable(GL_NORMALIZE); //enable normalizing normal vectors
    gluOrtho2D(0,800,600,0); //set draw coordinates
    glEnable(GL_DEPTH_TEST); //enable depth test
    char *name;
    argv[1] = strtok(argv[1],"/");
    while(argv[1] != NULL){
      name = argv[1];
      argv[1] = strtok(NULL,"/");
    }
    snprintf(trunc_file,sizeof(trunc_file),"view/trunc_%s",name);
    snprintf(filename,sizeof(filename),"view/%s",name);
    printf("filename = %s\n",filename);
    printf("trunc_file = %s\n",trunc_file);
    glutMainLoop(); //enter the main loop
    return 0;
  }
}
//=========================================================

//=========================================================
// MENU //
//=========================================================
void create_menu(){
  int sub_rot = glutCreateMenu(menu); //create a submenu
  glutAddMenuEntry("rotation_Ox(x)",3); //add sub menu entry
  glutAddMenuEntry("rotation_Oy(y)",4); //add sub menu entry
  glutAddMenuEntry("rotation_Oz(z)",5); //add sub menu entry
  glutAddMenuEntry("inverse_angle(i)",7); //add sub menu entry
  glutAddMenuEntry("stop_animation(space)",6); //add sub menu entry
  int menid = glutCreateMenu(menu); //create the menu
  glutAddMenuEntry("clear(c)",1); //create menu entry
  glutAddMenuEntry("build(b)",2); //create menu entry
  glutAddSubMenu("animation",sub_rot); //create menu entry
  glutAddMenuEntry("truncate(t)",10); //truncate R
  glutAddMenuEntry("exit(Esc)",9); //create menu entry
  //let the menu respond on the right mouse button
  glutAttachMenu(GLUT_RIGHT_BUTTON);
}
//---------------------------------------------------------
void menu(int value){
  primitive = value; //set menu identifier
  glutPostRedisplay(); //redraw now
}
//=========================================================
// RESHAPING WINDOW //
//=========================================================
void reshape(int w, int h){
  glViewport(0,0,w,h); //set the viewport
  glMatrixMode(GL_PROJECTION); //sets current matrix mode to projection
  glLoadIdentity(); //replace current matrix with the identity matrix
  gluPerspective(60,(double)w/h,1,500); //set up perspective projection matrix
  //define a viewing transformation
  gluLookAt(45,20,50,0,0,0,0,1,0); //eye (x,y,z) center (x,y,z) up(x,y,z)
  glMatrixMode(GL_MODELVIEW); //sets current matrix mode to modelview
  glLoadIdentity(); //replace current matrix with the identity matrix
}
//=========================================================
// DISPLAY //
//=========================================================
void disp(){
  //clean color and depth buffer
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  switch(primitive){ //choose drawing on event
	case 2: //just draw Cube
    R = load_source(filename); //get source mine-field
    dim = dim_R;
    build_R(R,dim_R);
    break;
  case 3: //rotation Ox
    rotation(1,0,0,rot_angle); //Ox
    glutTimerFunc(1000/360, timer, 0); //registers a timer callback 
    break;
  case 4: //rotation Oy
    rotation(0,1,0,rot_angle); //Oy
    glutTimerFunc(1000/360, timer, 0); //registers a timer callback 
    break;
  case 5: //rotation Oz
    rotation(0,0,1,rot_angle); //Oz
    glutTimerFunc(1000/360, timer, 0); //registers a timer callback 
    break;
  case 6: //just draw Cube of current dim
    build_R(R,dim);
    break;
  case 7: //inverse rotation angle
    rot_angle = -rot_angle;
    primitive = 6;
    break;
  case 8: //rotation by mouse
    glLoadIdentity();
    glRotatef(xrot, 1.0f, 0.0f, 0.0f); 
    glRotatef(yrot, 0.0f, 1.0f, 0.0f);
    build_R(R,dim);
    break;
  case 9: //exit
    glutDestroyWindow(win);
    exit(0);
  case 10: //trunc
    R = load_source(trunc_file);
    dim = dim_R;
    build_R(R,dim);
    break;
  };
  glFlush();
  glutSwapBuffers(); //swap buffers
}
//=========================================================
// MOUSE ACTIONS //
//=========================================================
void mouse(int button, int state, int x, int y){
  if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){
    mouseDown = true;
    xdiff = x - yrot;
    ydiff = -y + xrot;
    primitive = 8;
  }else{
    mouseDown = false;
  }
}
//---------------------------------------------------------
void mouseMotion(int x, int y){
  if(mouseDown){
    yrot = x - xdiff;
    xrot = y + ydiff;
    glutPostRedisplay();
  }
}
//=========================================================
// KEY ACTIONS //
//=========================================================
void keyb(unsigned char key, int x, int y){
  if(key == 27) primitive = 9; //exit
  if(key == 'c') primitive = 1; //clear window
  if(key == 'b') primitive = 2; //build mine-field
  if(key == 'x') primitive = 3; //rotation Ox
  if(key == 'y') primitive = 4; //rotation Oy
  if(key == 'z') primitive = 5; //rotation Oz
  if(key == 'a'){
    if(dim < dim_R){
      dim++;
      primitive = 6;
    }
  }
  if(key == 'd'){
    if(dim >= 2){
      dim--;
      primitive = 6;
    }
  }
  if(key == '+') scale(1.1,1.1,1.1); //scaling +
  if(key == '-') scale(0.9,0.9,0.9); //scaling -
  if(key == ' ') primitive = 6; //pause animation
  if(key == 'i') primitive = 7;
  if(key == 't') primitive = 10;
  glutPostRedisplay(); //redraw now
}
//---------------------------------------------------------
void special_keyboard(int key, int x, int y){
  if(key == GLUT_KEY_F1){
    fullscreen = !fullscreen;
    if(fullscreen){
      glutFullScreen();
    }else{
      glutReshapeWindow(800,600);
      glutPositionWindow(100,50);
    }
  }
}
//=========================================================
// MOVES //
//=========================================================
// animation //
//---------------------------------------------------------
void timer(int value){
  glutPostRedisplay(); //redraw now
  glutTimerFunc(1000/360, timer, 0); //registers a timer callback
}
//---------------------------------------------------------
// rotation //
//---------------------------------------------------------
void rotation(int x, int y, int z, double angle){
  glRotatef(angle, x, y, z); //* current matrix by a rotation matrix
  build_R(R,dim);
}
//---------------------------------------------------------
// scale //
//---------------------------------------------------------
void scale(float x, float y, float z){
  glScalef(x,y,z); //* current matrix by a scale matrix
  build_R(R,dim);
}